-
-
Notifications
You must be signed in to change notification settings - Fork 217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
respect rate limits during pagination by sleeping #167
Conversation
This is a good start, it needs some tests, CHANGELOG and such. Maybe we can also hint a sleep value in the options passed into the function and avoid |
That's a good idea about hinting a sleep value, though we may find it hard to tune to avoid the errors. The reason I say this is I asked some Slack folks about their rate limiting a while back and they had this to say:
Given this context, I think the best solution is to allow the individual dev to pass in their own default sleep time, so that they can tune to their use case, but not have any default sleep behavior beyond the rate limiting. |
I agree, lets default the hint to zero. I wonder whether retry behavior could be optionally built into everything? As a client I just don't want to deal with it :) But that can be a later PR/feature - feel free to open an issue for that too. |
I think this is what you had in mind for hinting a sleep value. I went with |
16dc964
to
812e78f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting much closer, hang on in there. Something should also be added in README around pagination about this, at least to document the new pause option(s).
CHANGELOG.md
Outdated
@@ -1,5 +1,6 @@ | |||
### 0.9.2 (Next) | |||
|
|||
* [#167](https://github.com/slack-ruby/slack-ruby-client/pull/167): Respect rate limits during pagination by sleeping. Also add optional `pause` parameter in order to proactively sleep between each paginated request - [@jmanian](https://github.com/jmanian). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe simply "Added support for pausing between paginated requests that cause Slack rate limiting"?
lib/slack/web/pagination/cursor.rb
Outdated
@@ -7,23 +7,31 @@ class Cursor | |||
|
|||
attr_reader :client | |||
attr_reader :verb | |||
attr_reader :pause |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel a bit uncomfortable calling this pause
because it's unclear whether that's a verb or a noun, maybe sleep_interval
? Don't feel strongly about this one though.
lib/slack/web/pagination/cursor.rb
Outdated
response = client.send(verb, query) | ||
rescue Slack::Web::Api::Errors::TooManyRequestsError => e | ||
sleep(e.retry_after.seconds) | ||
next |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also think we should have a max retry count with some default set fairly high. If slack blacklists you somehow you'll have code that never ends and it will be impossible to debug.
I also think there should be a log line here in DEBUG mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think the count should reset after a successful request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so.
3406f4a
to
549563b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Last problem with params, then I'm merging. Thanks!
lib/slack/web/pagination/cursor.rb
Outdated
attr_reader :params | ||
|
||
def initialize(client, verb, params = {}) | ||
@client = client | ||
@verb = verb | ||
@sleep_interval = params.delete(:sleep_interval) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This modifies incoming params
, ie. has a side-effect, you don't want to do that, so just params[:sleep_interval]
.
retry_count += 1 | ||
sleep(e.retry_after.seconds) | ||
next | ||
end | ||
yield response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can move this inside the rescue block and avoid next
, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also did I tell you that I was a nitpicky code reviewer? Hang on tight :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand, I'm the same way 😎.
Which part are you suggesting go in the rescue block? I started out with everything inside the begin block (see first commit b75bfcb) which avoids the next
. Is this what you mean?
I changed it (8542d00) because I thought it was bad practice to have more lines than necessary inside the begin
block — even if just for clarity (so that it's clear which line is expected to raise the error). But in this case it's obvious where the error is coming from, so I guess it's fine either way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exactly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually now think yours is better. Someone can do something inside the yield that causes that exception and we're going to have bad side effects and catching an error we shouldn't.
Merged. Thanks! |
I made a small change to dup the params and use delete in cdb697b. |
I was debating just leaving those parameters in there, because I think all the API methods ignore those extraneous parameters. But it felt more right to remove them. |
I opened #168 - interested in giving that one a try? |
@dblock Is this problem related to login/authentication stage? I noticed that when I deployed the bot ( However, on AWS ec2 where the network is extremely fast, the first 2 requests to Slack happen in within 1 second (I can see this when I turn on DEBUG mode), and then Slack simply blocks the future 3rd request. The bot will never be able to authenticate to the service. |
@kyanh-huynh #168 would mitigate that as well. |
resolves #166
Here's an initial attempt at respecting rate limits during pagination. No tests here yet.